/************************************************************************
* time.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"

#include <time.h>

#ifndef WIN32
#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#else
#include <windows.h>
#endif

#ifndef WIN32
static struct timeval start;
#else
static DWORD start;
#define TIME_WRAP_VALUE	(~(DWORD)0)
#endif

/* initialise time */
void time_init()
{
#ifndef WIN32
	gettimeofday(&start, NULL);
#else
	timeBeginPeriod(1);
	start = timeGetTime();
#endif
}

/* get number of milliseconds since start */
uint32_t time_ticks(void)
{
#ifndef WIN32
	uint32_t ticks;
	struct timeval now;
	gettimeofday(&now, NULL);
	ticks = (now.tv_sec-start.tv_sec)*1000+(now.tv_usec-start.tv_usec)/1000;
	return ticks;
#else
	DWORD now = timeGetTime();
	DWORD ticks;
	if (now < start) {
		ticks = (TIME_WRAP_VALUE-start) + now;
	}else{
		ticks = (now - start);
	}

	return ticks;
#endif
}

/* high resolution sleep() */
void delay(uint32_t ms)
{
#ifndef WIN32
	struct timespec elapsed;
	struct timespec tv;
	int was_error;

	/* Set the timeout interval */
	elapsed.tv_sec = ms/1000;
	elapsed.tv_nsec = (ms%1000)*1000000;
	do {
		errno = 0;

		tv.tv_sec = elapsed.tv_sec;
		tv.tv_nsec = elapsed.tv_nsec;
		was_error = nanosleep(&tv, &elapsed);
	} while (was_error && (errno == EINTR));
#else
	Sleep(ms);
#endif
}

/*
 * for animation timing
 * returns the seconds since last
 */
float time_dtime(uint32_t last)
{
	uint32_t now = time_ticks();
	uint32_t elapsed = now-last;
	return (float)elapsed/1000.0;
}

/* essentially a frame capper */
uint32_t interval_delay(uint32_t last, uint32_t hz)
{
	if (last) {
		uint32_t now = time_ticks();
		uint32_t elapsed = now-last;
		uint32_t timer = 1000/hz;
		uint32_t idelay;
		/*
		 * If elapsed is greater than timer, this could really mess up
		 * the frame rate due to a negative value rolling over to an
		 * insanely high positive value, so only delay if we have to.
		 */
		if (timer > elapsed) {
			idelay = timer-elapsed;
			delay(idelay);
		}
	}
	return time_ticks();
}

/* get the frames per second */
uint32_t calc_fps(uint32_t prev, uint32_t current)
{
	uint32_t diff = (current-prev);
	if (diff) {
		return 1000/diff;
	}else{
		return 1000;
	}
}
